<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>Go + App Engine</title>
<script src='http://html5slides.googlecode.com/svn/trunk/slides.js'></script>
<script>
      addPrettify = function() {}
    </script>
<style>.swiss{font-weight:bold;background:#DD4B39;color:white;border-radius:5px;padding:5px;}.photo{position:absolute;right:100px;top:350px;border-radius:10px;}a{text-decoration:none;}.slides>article .selflink{font-size:24px;display:block;position:absolute;left:15px;bottom:40px;}.slides>article.nobackground .selflink{display:none}pre,code{font-family:Menlo,monospace;font-size:14px;}pre{line-height:18px;}pre .comment{color:#375EAB;}pre .highlight,pre .highlight-comment,pre .selection-highlight,pre .selection-highlight-comment{background:#FFFF00;}pre .selection,pre .selection-comment{background:#FF9632;}pre .ln{color:#999;}</style>
</head>
<body style='display: none'>
<section class='slides layout-regular template-default'>
<article>
<h1>
Go <img src="http://weekly.golang.org/doc/gopher/frontpage.png" height="64px"> + App Engine <img src="images/appengine_final.png" height="64px"> = <img src="http://code.google.com/p/appengine-go/logo?cct=1325616189">
</h1>
<p>
Johan Euphrosine
<br>
2012年3月15日
</p>
<a class="selflink" href="http://proppy-go-ae.appspot.com/">proppy-go-ae.appspot.com</a>
</article>
<article>
<h3>关于我</h3>
<p>
<ul>
<img class="photo" src="images/proppy.jpg">
<li>Johan Euphrosine &lt;proppy&#64;google.com&gt;</li>
<li>Google <span class="swiss">苏黎世</span>办公室</li>
<li>开发者项目工程师</li>
<li>App Engine <img src="images/appengine_final.png" height="24px"> 开发者社区支持人员</li>
<li><a href="http://profiles.google.com/proppy">profiles.google.com/proppy</a></li>
</ul>
</p>
<a class="selflink" href="http://proppy-go-ae.appspot.com/">proppy-go-ae.appspot.com</a>
</article>
<article>
<h3>
App Engine
</h3>
<p>
App Engine 允许用户在 Google 的基础设施之上构建自己的 web 应用
</p>
<a class="selflink" href="http://proppy-go-ae.appspot.com/">proppy-go-ae.appspot.com</a>
</article>
<article>
<h3>
Go Runtime
</h3>
<p>
<ul>
<li>原生代码</li>
<li>低内存消耗</li>
<li>并行设计</li>
</ul>
</p>
<a class="selflink" href="http://proppy-go-ae.appspot.com/">proppy-go-ae.appspot.com</a>
</article>
<article class="nobackground">
<h3>
Hello Gopher!
</h3>
<pre> package gopher

import (
    &#34;fmt&#34;
    &#34;net/http&#34;
)

// init 函数将在应用启动时调用。
func init() {
    // 为 /hello 这个 URL 注册接管函数。
    http.HandleFunc(&#34;/hello&#34;, hello)
}

// hello 是 HTTP 接管函数，将输出字符串“Hello Gopher!”
func hello(w http.ResponseWriter, r *http.Request) {
    fmt.Fprint(w, &#34;Hello, Gopher!&#34;)
}</pre>
<a class="selflink" href="http://proppy-go-ae.appspot.com/">proppy-go-ae.appspot.com</a>
</article>
<article>
<h3>
Hello App Engine!
</h3>
<pre> application: gopher
version: 1
runtime: go
api_version: go1beta

handlers:
- url: /images
  static_dir: images
- url: /doc
  static_dir: doc
- url: /.*
  script: _go_app</pre>
<pre>$ dev_appserver.py myapp/</pre>
<pre>$ appcfg.py update myapp/</pre>
<a class="selflink" href="http://proppy-go-ae.appspot.com/">proppy-go-ae.appspot.com</a>
</article>
<article>
<h3>
演示
</h3>
<p>
<a href="/hello">Hello Gopher!</a>
</p>
<a class="selflink" href="http://proppy-go-ae.appspot.com/">proppy-go-ae.appspot.com</a>
</article>
<article>
<h3>
Hello hackernews! （XML 解析）
</h3>
<pre> package gopher

import (
    &#34;appengine&#34;
    &#34;appengine/urlfetch&#34;
    &#34;encoding/xml&#34;
    &#34;fmt&#34;
    &#34;net/http&#34;
    &#34;time&#34;
)

func init() {
    // 为 /hackernews 这个 URL 注册接管函数。
    http.HandleFunc(&#34;/hackernews&#34;, hackernews)
}</pre>
<a class="selflink" href="http://proppy-go-ae.appspot.com/">proppy-go-ae.appspot.com</a>
</article>
<article class="nobackground">
<h3>
将 XML 映射为 Go 数据类型
</h3>
<pre> &lt;rss version=&#34;2.0&#34;&gt;
  &lt;channel&gt;
    &lt;item&gt;
      &lt;title /&gt;
      &lt;link /&gt;
...</pre>
<pre> type HNFeed struct {
    Data HNFeedData `xml:&#34;channel&#34;`
}

type HNFeedData struct {
    Items []Item `xml:&#34;item&#34;`
}

type Item struct {
    Title string `xml:&#34;title&#34;`
    Url   string `xml:&#34;link&#34;`
}</pre>
<a class="selflink" href="http://proppy-go-ae.appspot.com/">proppy-go-ae.appspot.com</a>
</article>
<article class="nobackground">
<h3>
解码
</h3>
<pre> func hackernewsItems(c appengine.Context) []Item {
    client := urlfetch.Client(c)
    resp, err := client.Get(HNFeedUrl)
    if err != nil {
        c.Errorf(&#34;装取 %s 出错: %s&#34;, HNFeedUrl, err.Error())
        return []Item{}
    }
    defer resp.Body.Close()
    decoder := xml.NewDecoder(resp.Body)
    var feed *HNFeed
    if err = decoder.Decode(&amp;feed); err != nil {
        c.Errorf(&#34;解码 HNFeed 出错: %s&#34;, err.Error())
        return []Item{}
    }
    return feed.Data.Items
}</pre>
<a class="selflink" href="http://proppy-go-ae.appspot.com/">proppy-go-ae.appspot.com</a>
</article>
<article class="nobackground">
<h3>
并输出
</h3>
<pre> func hackernews(w http.ResponseWriter, r *http.Request) {
    c := appengine.NewContext(r)
    w.Header().Add(&#34;Content-Type&#34;, &#34;text/plain&#34;)
    for _, item := range hackernewsItems(c) {
        fmt.Fprintf(w, &#34;%s: %s\n&#34;, item.Title, item.Url)
    }
}</pre>
<a class="selflink" href="http://proppy-go-ae.appspot.com/">proppy-go-ae.appspot.com</a>
</article>
<article>
<h3>
演示
</h3>
<p>
<a href="/hackernews">Hello Hacker News!</a>
</p>
<a class="selflink" href="http://proppy-go-ae.appspot.com/">proppy-go-ae.appspot.com</a>
</article>
<article>
<h3>
Hello proggit! （JSON 解析）
</h3>
<pre> package gopher

import (
    &#34;appengine&#34;
    &#34;appengine/urlfetch&#34;
    &#34;encoding/json&#34;
    &#34;fmt&#34;
    &#34;net/http&#34;
    &#34;time&#34;
)

func init() {
    // 为 /proggit 这个 URL 注册接管函数。
    http.HandleFunc(&#34;/proggit&#34;, proggit)
}</pre>
<a class="selflink" href="http://proppy-go-ae.appspot.com/">proppy-go-ae.appspot.com</a>
</article>
<article class="nobackground">
<h3>
将 Json 映射为 Go 数据类型
</h3>
<pre>
{"data": { "children": [
   {"data": { "title": ... , "url": ...}}, ...</pre>
<pre> type RedditFeed struct {
    Data RedditFeedData
}

type RedditFeedData struct {
    Items []RedditFeedItem `json:&#34;children&#34;`
}

type RedditFeedItem struct {
    Data Item
}</pre>
<pre> type Item struct {
    Title string `xml:&#34;title&#34;`
    Url   string `xml:&#34;link&#34;`
}</pre>
<a class="selflink" href="http://proppy-go-ae.appspot.com/">proppy-go-ae.appspot.com</a>
</article>
<article class="nobackground">
<h3>
解码
</h3>
<pre> func proggitItems(c appengine.Context) []Item {
    client := urlfetch.Client(c)
    resp, err := client.Get(ProggitFeedUrl)
    if err != nil {
        c.Errorf(&#34;装取 %s 出错: %s&#34;, ProggitFeedUrl, err.Error())
        return []Item{}
    }
    defer resp.Body.Close()
    decoder := json.NewDecoder(resp.Body)
    var feed *RedditFeed
    if err = decoder.Decode(&amp;feed); err != nil {
        c.Errorf(&#34;解码 RedditFeed 出错: %s&#34;, err.Error())
        return []Item{}
    }
    items := make([]Item, len(feed.Data.Items))
    for i, item := range feed.Data.Items {
        items[i] = item.Data
    }
    return items
}</pre>
<a class="selflink" href="http://proppy-go-ae.appspot.com/">proppy-go-ae.appspot.com</a>
</article>
<article class="nobackground">
<h3>
并输出
</h3>
<pre> func proggit(w http.ResponseWriter, r *http.Request) {
    c := appengine.NewContext(r)
    w.Header().Add(&#34;Content-Type&#34;, &#34;text/plain&#34;)
    for _, item := range proggitItems(c) {
        fmt.Fprintf(w, &#34;%s: %s\n&#34;, item.Title, item.Url)
    }
}</pre>
<a class="selflink" href="http://proppy-go-ae.appspot.com/">proppy-go-ae.appspot.com</a>
</article>
<article>
<h3>
演示
</h3>
<p>
<a href="/proggit">Hello Proggit!</a>
</p>
<a class="selflink" href="http://proppy-go-ae.appspot.com/">proppy-go-ae.appspot.com</a>
</article>
<article>
<h3>
Progginator! （整合）
</h3>
<pre> func progginator(w http.ResponseWriter, r *http.Request) {
    c := appengine.NewContext(r)
    hnItems := hackernewsItems(c)
    pgItems := proggitItems(c)
    w.Header().Add(&#34;Content-Type&#34;, &#34;text/plain&#34;)
    for _, item := range hnItems {
        fmt.Fprintf(w, &#34;%s: %s\n&#34;, item.Title, item.Url)
    }
    for _, item := range pgItems {
        fmt.Fprintf(w, &#34;%s: %s\n&#34;, item.Title, item.Url)
    }
}</pre>
<a class="selflink" href="http://proppy-go-ae.appspot.com/">proppy-go-ae.appspot.com</a>
</article>
<article>
<h3>
演示
</h3>
<p>
<a href="/progginator">Progginator!</a>
<a class="selflink" href="http://proppy-go-ae.appspot.com/">proppy-go-ae.appspot.com</a>
</p>
<p>命令行输出：</p>
<pre>
2012-03-15 04:17:59.148 hackernews: 261.695ms
2012-03-15 04:17:59.246 proggit: 97.961ms
2012-03-15 04:17:59.246 progginator: 359.822ms
</pre>
</article>
<article class="nobackground">
<h3>
并行处理
</h3>
<pre> func progginator_(w http.ResponseWriter, r *http.Request) {
    c := appengine.NewContext(r)
    results := make(chan []Item)
    go func() {
        results &lt;- hackernewsItems(c)
    }()
    go func() {
        results &lt;- proggitItems(c)
    }()
    w.Header().Add(&#34;Content-Type&#34;, &#34;text/plain&#34;)
    for i := 0; i &lt; 2; i++ {
        items := &lt;-results
        for _, item := range items {
            fmt.Fprintf(w, &#34;%s: %s\n&#34;, item.Title, item.Url)
        }
    }
}</pre>
<a class="selflink" href="http://proppy-go-ae.appspot.com/">proppy-go-ae.appspot.com</a>
</article>
<article>
<h3>
演示
</h3>
<p>
<a href="/progginator_">Progginator!</a>
</p>
<p>命令行输出：</p>
<pre>
2012-03-15 04:18:03.804 proggit: 110.451ms
2012-03-15 04:18:03.887 hackernews: 193.751ms
2012-03-15 04:18:03.887 progginator_: 193.824ms
</pre>
<a class="selflink" href="http://proppy-go-ae.appspot.com/">proppy-go-ae.appspot.com</a>
</article>
<article>
<h3>
哇！
</h3>
<ul>
<li><code>encoding/*</code> 这些包让数据解析重新焕发趣味!</li>
<li>Go runtime 有并行处理的天赋</li>
<li>由于 goroutine 与 channel 的内存模型设计，同异步 API 也不需要了</li>
</ul>
<a class="selflink" href="http://proppy-go-ae.appspot.com/">proppy-go-ae.appspot.com</a>
</article>
<article>
<h3>
课后作业
</h3>
<ul>
<li>滤除重复内容，增设排序处理</li>
<li>通过模板与 css 美化应用</li>
<li>用 Task Queue 异步装取数据源</li>
<li>添加缓存机制!</li>
<li>去 fork 这团代码: <a href="http://code.google.com/p/progginator">code.google.com/p/progginator</a>
</ul>
<a class="selflink" href="http://proppy-go-ae.appspot.com/">proppy-go-ae.appspot.com</a>
</article>
<article>
<h3>
谢谢观看!
</h3>
<pre>
	<img src="http://code.google.com/p/appengine-go/logo?cct=1325616189" height="40px"> := "<a href='http://code.google.com/appengine/docs/go/'>code.google.com/appengine/docs/go/</a>"
	<img src="http://weekly.golang.org/doc/gopher/frontpage.png" height="40px"> := "<a href='http://golang.org'>golang.org</a>"
	<img src="images/proppy.jpg" height="40px"> := "<a href='http://profiles.google.com/proppy'>profiles.google.com/proppy</a>"
</pre>
<small style="position:fixed; bottom: 16%;">翻译：<a href="http://lidian.info">xslidian</a><a href="http://cloudflare.com/email-protection.html#2b535847424f424a456b4c5e4a44054340">@</a><a href="http://guao.hk">guao.hk</a></small>
<a class="selflink" href="http://proppy-go-ae.appspot.com/">proppy-go-ae.appspot.com</a>
</article>
</section>
<script type="text/javascript">
/* <![CDATA[ */
(function(){try{var s,a,i,j,r,c,l=document.getElementsByTagName("a"),t=document.createElement("textarea");for(i=0;l.length-i;i++){try{a=l[i].getAttribute("href");if(a&&"cloudflare.com/email-protection"==a.substr(7 ,31)){s='';j=44;r=parseInt(a.substr(j,2),16);for(j+=2;a.length-j&&a.substr(j,1)!='X';j+=2){c=parseInt(a.substr(j,2),16)^r;s+=String.fromCharCode(c);}j+=1;s+=a.substr(j,a.length-j);t.innerHTML=s.replace(/</g,"&lt;").replace(/>/g,"&gt;");l[i].setAttribute("href","mailto:"+t.value);}}catch(e){}}}catch(e){}})();
/* ]]> */
</script>
</body>
</html>
